//
// Created by 覃州 on 2024/1/26.
//

#ifndef CRCANON_IOC_CONTAINER_H
#define CRCANON_IOC_CONTAINER_H

#include <functional>
#include <string>
#include <unordered_map>
#include "any.h"

namespace Canon::Component {

using namespace std;

class IocContainer
{
public:
    IocContainer() = default;
    ~IocContainer() = default;

    template <class T, typename Depend, typename... Args>
    typename std::enable_if<!std::is_base_of<T, Depend>::value>::type RegisterType(
        const string& key)
    {
        std::function<T*(Args...)> function = [](Args... args) {
            return new T(new Depend(args...));
        };
        Register(key, static_cast<Any>(function));
    }

    template <class T, typename Depend, typename... Args>
    typename std::enable_if<std::is_base_of<T, Depend>::value>::type RegisterType(const string& key)
    {
        std::function<T*(Args...)> function = [](Args... args) { return new Depend(args...); };
        Register(key, static_cast<Any>(function));
    }

    template <class T, typename... Args>
    std::shared_ptr<T> ResolveShared(const string& key, Args... args)
    {
        T* t = Resolve<T>(key, args...);
        return std::shared_ptr<T>(t);
    }

private:
    template <class T, typename... Args>
    T* Resolve(const string& key, Args... args)
    {
        if (creatorMap.find(key) == creatorMap.end()) {
            return nullptr;
        }
        Any resolve = creatorMap[key];
        std::function<T*(Args...)> function = resolve.AnyCast<std::function<T*(Args...)>>();
        return function(args...);
    }

    void Register(const string& key, Any constructor)
    {
        if (creatorMap.find(key) != creatorMap.end()) {
            throw std::invalid_argument("this key has already exist!");
        }
        creatorMap.emplace(key, constructor);
    }

private:
    unordered_map<string, Any> creatorMap;
};
} // namespace Canon::Component

#endif // CRCANON_IOC_CONTAINER_H
